package com.hedl.classroom.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.api.utils.StringUtils;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hedl.classroom.base.exception.OnlinecClassroomException;
import com.hedl.classroom.config.AlipayConfig;
import com.hedl.classroom.config.PayNotifyConfig;
import com.hedl.classroom.dto.AliReturnPaymentDataDto;
import com.hedl.classroom.dto.PayRecordDto;
import com.hedl.classroom.dto.PayStatusDto;
import com.hedl.classroom.mapper.XcOrdersMapper;
import com.hedl.classroom.mapper.XcPayRecordMapper;
import com.hedl.classroom.model.po.MqMessage;
import com.hedl.classroom.po.ReturnPaymentData;
import com.hedl.classroom.po.XcOrders;
import com.hedl.classroom.po.XcPayRecord;
import com.hedl.classroom.service.MqMessageService;
import com.hedl.classroom.service.OrderPaymentService;
import com.hedl.classroom.service.OrderService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import static java.lang.System.out;

/**
 * @author Big Watermelon
 * @description 订单支付接口
 * @date 2023/12/18/23:58
 */
@Service
public class OrderPaymentServiceImpl implements OrderPaymentService {

    @Value("${pay.alipay.APP_ID}")
    private String APP_ID;

    @Value("${pay.alipay.APP_PRIVATE_KEY}")
    private String APP_PRIVATE_KEY;

    @Value("${pay.alipay.ALIPAY_PUBLIC_KEY}")
    private String ALIPAY_PUBLIC_KEY;

    @Resource
    private XcPayRecordMapper xcPayRecordMapper;

    @Resource
    private XcOrdersMapper xcOrdersMapper;

    @Resource
    OrderPaymentServiceImpl orderPaymentServiceProxy;

    @Resource
    private OrderService orderService;

    @Resource
    private MqMessageService mqMessageService;

    @Override
    public void requestpay(String payNo,HttpServletResponse httpServletResponse) throws AlipayApiException, IOException {
        XcPayRecord xcPayRecord = getXcPayRecordByPayNo(payNo);
        //支付结果
        String status = xcPayRecord.getStatus();
        if ("601002".equals(status)){
            OnlinecClassroomException.cast("已支付，无需重复支付！");
        }

        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, APP_ID, APP_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);
        //获得初始化的AlipayClient
        AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();//创建API对应的request

        //alipayRequest.setReturnUrl("http://domain.com/CallBack/return_url.jsp");
        //在公共参数中设置回跳和通知地址
        //alipayRequest.setNotifyUrl("http://domain.com/CallBack/notify_url.jsp");

        //这里无法测试，需要公网ip，但是在单元测试中我们可以通过主动调取支付宝获得结果，是一样的
        //request.setNotifyUrl("http://192.168.1.9:63030/orders/paynotify");    //接收支付结果通知，需要开启公网ip，或者内网穿透，无法测试，需要测试打开即可

        //金额
        Float totalPrice = xcPayRecord.getTotalPrice();
        //订单名称
        String orderName = xcPayRecord.getOrderName();
        //填充业务参数
        JSONObject bizContent = new JSONObject();
        //商户订单号，商家自定义，保持唯一性
        bizContent.put("out_trade_no",payNo );
        //支付金额，最小值0.01元
        bizContent.put("total_amount", totalPrice);
        //订单标题，不可使用特殊符号
        bizContent.put("subject", orderName);
        //可选参数
        bizContent.put("product_code", "QUICK_WAP_WAY");

        request.setBizContent(bizContent.toString());

        //调用SDK生成表单
        //String form = alipayClient.pageExecute(request).getBody();
        String form = alipayClient.pageExecute(request).getBody();

        httpServletResponse.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
        //直接将完整的表单html输出到页面
        httpServletResponse.getWriter().write(form);
        httpServletResponse.getWriter().flush();
    }

    @Override
    public PayRecordDto payresult(String payNo) throws AlipayApiException {
        if (StringUtils.isEmpty(payNo)) {
            OnlinecClassroomException.cast("请先完成支付！");
        }
        //调用支付宝的接口查询支付结果
        PayStatusDto payStatusDto = queryPayResultFromAlipay(payNo);

        //System.out.println(payStatusDto);

        //拿到支付的结果更新支付记录表和订单表的支付状态
        orderPaymentServiceProxy.saveAliPayStatus(payStatusDto);

        //返回最新的支付记录
        XcPayRecord xcPayRecord = getXcPayRecordByPayNo(payNo);
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(xcPayRecord,payRecordDto);

        return payRecordDto;
    }

    private PayStatusDto queryPayResultFromAlipay(String payNo) {
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL,APP_ID,APP_PRIVATE_KEY,AlipayConfig.FORMAT,AlipayConfig.CHARSET,ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        //如下两个参数任选一个都可以查到交易记录
        bizContent.put("out_trade_no", payNo);    //商户订单号
        //bizContent.put("trade_no", "2023121722001434390501628084");   //订单号，在沙箱的账单中
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
            OnlinecClassroomException.cast("请求支付查询支付结果异常！");
        }
        if(response.isSuccess()){
            System.out.println("调用成功!");
            String form = response.getBody();
            AliReturnPaymentDataDto aliReturnPaymentDataDto = JSON.parseObject(form, AliReturnPaymentDataDto.class);
            ReturnPaymentData returnPaymentData = aliReturnPaymentDataDto.getAlipayTradeQueryResponse();

            //支付宝交易号
            String tradeNo = returnPaymentData.getTradeNo();
            //交易状态
            String tradeStatus = returnPaymentData.getTradeStatus();
            //总金额
            String totalAmount = returnPaymentData.getTotalAmount();

            //解析支付结果
            PayStatusDto payStatusDto = new PayStatusDto();
            payStatusDto.setOut_trade_no(payNo);
            payStatusDto.setTrade_no(tradeNo);
            payStatusDto.setTrade_status(tradeStatus);
            payStatusDto.setApp_id(APP_ID);
            payStatusDto.setTotal_amount(totalAmount);

            return payStatusDto;
        } else {
            OnlinecClassroomException.cast("请求支付宝查询支付结果失败！");
        }
        return null;
    }

    /**
     * 接收支付结果通知
     * @param request
     * @param response
     * @throws AlipayApiException
     * @throws IOException
     */
    @Override
    public void receivenotify(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException, IOException {
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }

        boolean verify_result = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, "RSA2");

        if(verify_result){//验证成功

            //商户订单号
            String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
            //支付宝交易号
            String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
            //交易状态
            String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
            //金额
            String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");

            if(trade_status.equals("TRADE_FINISHED")){
                PayStatusDto payStatusDto = new PayStatusDto();
                payStatusDto.setTrade_status(trade_status);
                payStatusDto.setTrade_no(trade_no);
                payStatusDto.setOut_trade_no(out_trade_no);
                payStatusDto.setTotal_amount(total_amount);
                payStatusDto.setApp_id(APP_ID);

                //更新支付记录表的状态为成功，订单表的状态为成功
                //注意这里是非事务方法
                orderPaymentServiceProxy.saveAliPayStatus(payStatusDto);
            }


            //返回结果
            response.getWriter().write("success");

        }else{//验证失败
            response.getWriter().write("fail");
        }

    }

    /**
     * 保存支付宝支付结果
     * @param payStatusDto 支付结果信息，从支付宝查询到的信息
     */
    @Transactional
    public void saveAliPayStatus(PayStatusDto payStatusDto){

        //支付记录号
        String payNo = payStatusDto.getOut_trade_no();
        XcPayRecord xcPayRecord = getXcPayRecordByPayNo(payNo);

        //拿到相关联的订单id
        Long orderId = xcPayRecord.getOrderId();

        XcOrders xcOrders = xcOrdersMapper.selectById(orderId);
        if (xcOrders == null){
            OnlinecClassroomException.cast("找不到相关联的订单！");
        }

        //支付状态
        String statusFromDb = xcPayRecord.getStatus();
        //如果数据支付的状态已经成功了，不在处理了
        if ("601002".equals(statusFromDb)) {
            //如果已成功
            return ;
        }

        //如果支付成功,从支付宝查询到的支付结果
        String tradeStatus = payStatusDto.getTrade_status();
        //支付宝返回的信息为支付成功
        if ("TRADE_SUCCESS".equals(tradeStatus)){
            //支付宝的订单号
            String tradeNo = payStatusDto.getTrade_no();

            //更新支付记录表的状态为支付成功
            xcPayRecord.setStatus("601002");
            xcPayRecord.setOutPayNo(tradeNo);
            //第三方支付渠道编号
            xcPayRecord.setOutPayChannel("Alipay");
            xcPayRecord.setPaySuccessTime(LocalDateTime.now());
            xcPayRecordMapper.updateById(xcPayRecord);
            //更新订单表的状态为支付成功
            xcOrders.setStatus("600002");   //订单状态为交易成功
            xcOrdersMapper.updateById(xcOrders);


            //将消息持久化到数据库
            //选课id
            String outBusinessId = xcOrders.getOutBusinessId();
            //订单类型
            String orderType = xcOrders.getOrderType();
            MqMessage mqMessage = mqMessageService.addMessage(PayNotifyConfig.MESSAGE_TYPE,outBusinessId,orderType,null);

            //发送消息
            orderService.notifyPayResult(mqMessage);
        }
    }

    /**
     * 查询支付记录
     * @param payNo 交易id
     * @return
     */
    private XcPayRecord getXcPayRecordByPayNo(String payNo){
        LambdaQueryWrapper<XcPayRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(XcPayRecord::getPayNo,payNo);
        XcPayRecord xcPayRecord = xcPayRecordMapper.selectOne(queryWrapper);
        if (xcPayRecord == null){
            OnlinecClassroomException.cast("支付记录不存在！");
        }
        return xcPayRecord;
    }
}
